home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_200 / 236_01 / bawk.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-06-05  |  12.5 KB  |  636 lines

  1. /*
  2.     HEADER:        CUG236;
  3.     TITLE:        BAWK Text Pattern/Action Tool (Brod86);
  4.     DATE:        05/17/1987;
  5.     DESCRIPTION:    "BAWK scans text files for regular expression patterns
  6.             and executes a user-defined action (C code fragment)
  7.             for each specified pattern found.  Descended from the
  8.             DECUS version of the same program.";
  9.     VERSION:    1.1;
  10.     KEYWORDS:    Text Filter;
  11.     FILENAME:    BAWK.C;
  12.     SEE-ALSO:    BAWK.H, BAWK.DOC, BAWKACT.C, BAWKDO.C, BAWKPAT.C,
  13.             BAWKSYM.C;
  14.     COMPILERS:    vanilla;
  15.     AUTHORS:    W. C. Colley III, B. Brodt;
  16.     WARNINGS:    "Program runs out of memory under CP/M Eco-C and dies
  17.             without a trace.  Aztec C II is OK.  Won't compile
  18.             yet under ECO-C88 -- module bawkdo.c gives a strange
  19.             code generator error.  REQUIRES that sizeof(int) ==
  20.             sizeof(char *)!!!!!  Therefore, the MSDOS small model
  21.             is OK, but the MSDOS large model is no good.";
  22. */
  23.  
  24. /*
  25.  * Bawk main program
  26.  */
  27. #define MAIN 1
  28. #include <stdio.h>
  29. #include "bawk.h"
  30.  
  31. unsigned _STACK = 10000;
  32.  
  33. /* Functions local to this module.    */
  34.  
  35. void compile(), newfile(), process(), usage();
  36.  
  37. /*
  38.  * Main program
  39.  */
  40. int main( argc, argv )
  41. int argc;
  42. char **argv;
  43. {
  44.     char gotrules, didfile, getstdin;
  45.  
  46.     getstdin =
  47.     didfile =
  48.     gotrules = 0;
  49.  
  50.     /*
  51.      * Initialize global variables:
  52.      */
  53.     Stackptr = Stackbtm - 1;
  54.     Stacktop = Stackbtm + MAXSTACKSZ;
  55.     Nextvar = Vartab;
  56.  
  57.     strcpy( Fldsep, " \t" );
  58.     strcpy( Rcrdsep, "\n" );
  59.  
  60.     /*
  61.      * Parse command line
  62.      */
  63.     while ( --argc )
  64.     {
  65.         if ( **(++argv) == '-' )
  66.         {
  67.             /*
  68.              * Process dash options.
  69.              */
  70.             switch (*++(*argv))
  71.             {
  72. #ifdef DEBUG
  73.             case 'D':
  74.             case 'd':
  75.                 ++Debug;
  76.                 break;
  77. #endif
  78.             case 0:
  79.                 ++getstdin;
  80.                 /* --argv; */
  81.                 goto dosomething;
  82.                 break;
  83.             default: usage();
  84.             }
  85.         }
  86.         else
  87.         {
  88. dosomething:
  89.             if ( gotrules )
  90.             {
  91.                 /*
  92.                  * Already read rules file - assume this is
  93.                  * is a text file for processing.
  94.                  */
  95.                 if ( ++didfile == 1 && Beginact )
  96.                     doaction( Beginact );
  97.                 if ( getstdin )
  98.                 {
  99.                     --getstdin;
  100.                     newfile( 0 );
  101.                 }
  102.                 else
  103.                     newfile( *argv );
  104.                 process();
  105.             }
  106.             else
  107.             {
  108.                 /*
  109.                  * First file name argument on command line
  110.                  * is assumed to be a rules file - attempt to
  111.                  * compile it.
  112.                  */
  113.                 if ( getstdin )
  114.                 {
  115.                     --getstdin;
  116.                     newfile( 0 );
  117.                 }
  118.                 else
  119.                     newfile( *argv );
  120.                 compile();
  121.                 gotrules = 1;
  122.             }
  123.         }
  124.     }
  125.     if ( !gotrules )
  126.         usage();
  127.  
  128.     if ( ! didfile )
  129.     {
  130.         /*
  131.          * Didn't process any files yet - process stdin.
  132.          */
  133.         newfile( 0 );
  134.         if ( Beginact )
  135.             doaction( Beginact );
  136.         process();
  137.     }
  138.     if ( Endact )
  139.         doaction( Endact );
  140.     return 0;
  141. }
  142.  
  143. /*
  144.  * Regular expression/action file compilation routines.
  145.  */
  146. void compile()
  147. {
  148.     /*
  149.      * Compile regular expressions and C actions into Rules struct,
  150.      * reading from current input file "Fileptr".
  151.      */
  152.     int c, len;
  153.  
  154. #ifdef DEBUG
  155.     if ( Debug )
  156.         error( "compiling...", 0 );
  157. #endif
  158.  
  159.     while ( (c = getcharacter()) != -1 )
  160.     {
  161.         if ( c==' ' || c=='\t' || c=='\n' )
  162.             /* swallow whitespace */
  163.             ;
  164.         else if ( c=='#' )
  165.         {
  166.             /*
  167.              * Swallow comments
  168.              */
  169.             while ( (c=getcharacter()) != -1 && c!='\n' )
  170.                 ;
  171.         }
  172.         else if ( c=='{' )
  173.         {
  174. #ifdef DEBUG
  175.         if ( Debug ) {
  176.                 printf ("got curly brace\n");
  177.                 error( "action", 0 );
  178.         }
  179. #endif
  180.             /*
  181.              * Compile (tokenize) the action string into our
  182.              * global work buffer, then allocate some memory
  183.              * for it and copy it over.
  184.              */
  185.             ungetcharacter( '{' );
  186.             len = act_compile( Workbuf );
  187.  
  188.             if ( Rulep && Rulep->action )
  189.             {
  190.                 Rulep->nextrule =
  191.                     (RULE *)getmem(sizeof(RULE));
  192.                 Rulep = Rulep->nextrule;
  193.                 fillmem( Rulep, sizeof(RULE), 0 );
  194.             }
  195.             if ( !Rulep )
  196.             {
  197.                 /*
  198.                  * This is the first action encountered.
  199.                  * Allocate the first Rules structure and
  200.                  * initialize it
  201.                  */
  202.                 Rules = Rulep =
  203.                     (RULE *)getmem(sizeof(RULE));
  204.                 fillmem( Rulep, sizeof(RULE), 0 );
  205.             }
  206.             Rulep->action = getmem( len );
  207.             movemem( Workbuf, Rulep->action, len );
  208.         }
  209.         else if ( c==',' )
  210.         {
  211. #ifdef DEBUG
  212.             if ( Debug )
  213.                 error( "stop pattern", 0 );
  214. #endif
  215.             /*
  216.              * It's (hopefully) the second part of a two-part
  217.              * pattern string.  Swallow the comma and start
  218.              * compiling an action string.
  219.              */
  220.             if ( !Rulep || !Rulep->pattern.start )
  221.                 error( "stop pattern without a start",
  222.                     RE_ERROR );
  223.             if ( Rulep->pattern.stop )
  224.                 error( "already have a stop pattern",
  225.                     RE_ERROR );
  226.             len = pat_compile( Workbuf );
  227.             Rulep->pattern.stop = getmem( len );
  228.             movemem( Workbuf, Rulep->pattern.stop, len );
  229.         }
  230.         else
  231.         {
  232.             /*
  233.              * Assume it's a regular expression pattern
  234.              */
  235. #ifdef DEBUG
  236.             if ( Debug )
  237.                 error( "start pattern", 0 );
  238. #endif
  239.  
  240.             ungetcharacter( c );
  241.             len = pat_compile( Workbuf );
  242.  
  243.             if ( *Workbuf == T_BEGIN )
  244.             {
  245.                 /*
  246.                  * Saw a "BEGIN" keyword - compile following
  247.                  * action into special "Beginact" buffer.
  248.                  */
  249.                 len = act_compile( Workbuf );
  250.                 Beginact = getmem( len );
  251.                 movemem( Workbuf, Beginact, len );
  252.                 continue;
  253.             }
  254.             if ( *Workbuf == T_END )
  255.             {
  256.                 /*
  257.                  * Saw an "END" keyword - compile following
  258.                  * action into special "Endact" buffer.
  259.                  */
  260.                 len = act_compile( Workbuf );
  261.                 Endact = getmem( len );
  262.                 movemem( Workbuf, Endact, len );
  263.                 continue;
  264.             }
  265.             if ( Rulep )
  266.             {
  267.                 /*
  268.                  * Already saw a pattern/action - link in
  269.                  * another Rules structure.
  270.                  */
  271.                 Rulep->nextrule =
  272.                     (RULE *)getmem(sizeof(RULE));
  273.                 Rulep = Rulep->nextrule;
  274.                 fillmem( Rulep, sizeof(RULE), 0 );
  275.             }
  276.             if ( !Rulep )
  277.             {
  278.                 /*
  279.                  * This is the first pattern encountered.
  280.                  * Allocate the first Rules structure and
  281.                  * initialize it
  282.                  */
  283.                 Rules = Rulep =
  284.                     (RULE *)getmem(sizeof(RULE));
  285.                 fillmem( Rulep, sizeof(RULE), 0 );
  286.             }
  287.             if ( Rulep->pattern.start )
  288.                 error( "already have a start pattern",
  289.                     RE_ERROR );
  290.  
  291.             Rulep->pattern.start = getmem( len );
  292.             movemem( Workbuf, Rulep->pattern.start, len );
  293.         }
  294.     }
  295.     endfile();
  296. }
  297.  
  298. /*
  299.  * Text file main processing loop.
  300.  */
  301. void process()
  302. {
  303.     /*
  304.      * Read a line at a time from current input file at "Fileptr",
  305.      * then apply each rule in the Rules chain to the input line.
  306.      */
  307.     int i;
  308.  
  309. #ifdef DEBUG
  310.     if ( Debug )
  311.         error( "processing...", 0 );
  312. #endif
  313.  
  314.     Recordcount = 0;
  315.  
  316.     while ( getline() )
  317.     {
  318.         /*
  319.          * Parse the input line.
  320.          */
  321.         Fieldcount = parse( Linebuf, Fields, Fldsep );
  322. #ifdef DEBUG
  323.         if ( Debug>1 )
  324.         {
  325.             printf( "parsed %d words:\n", Fieldcount );
  326.             for(i=0; i<Fieldcount; ++i )
  327.                 printf( "<%s>\n", Fields[i] );
  328.         }
  329. #endif
  330.  
  331.         Rulep = Rules;
  332.         do
  333.         {
  334.             if ( ! Rulep->pattern.start )
  335.             {
  336.                 /*
  337.                  * No pattern given - perform action on
  338.                  * every input line.
  339.                  */
  340.                 doaction( Rulep->action );
  341.             }
  342.             else if ( Rulep->pattern.startseen )
  343.             {
  344.                 /*
  345.                  * Start pattern already found - perform
  346.                  * action then check if line matches
  347.                  * stop pattern.
  348.                  */
  349.                 doaction( Rulep->action );
  350.                 if ( dopattern( Rulep->pattern.stop ) )
  351.                     Rulep->pattern.startseen = 0;
  352.             }
  353.             else if ( dopattern( Rulep->pattern.start ) )
  354.             {
  355.                 /*
  356.                  * Matched start pattern - perform action.
  357.                  * If a stop pattern was given, set "start
  358.                  * pattern seen" flag and process every input
  359.                  * line until stop pattern found.
  360.                  */
  361.                 doaction( Rulep->action );
  362.                 if ( Rulep->pattern.stop )
  363.                     Rulep->pattern.startseen = 1;
  364.             }
  365.         }
  366.         while ( Rulep = Rulep->nextrule );
  367.  
  368.         /*
  369.          * Release memory allocated by parse().
  370.          */
  371.         while ( Fieldcount )
  372.             free( Fields[ --Fieldcount ] );
  373.     }
  374. }
  375.  
  376. /*
  377.  * Miscellaneous functions
  378.  */
  379. int parse( str, wrdlst, delim )
  380. char *str;
  381. char *wrdlst[];
  382. char *delim;
  383. {
  384.     /*
  385.      * Parse the string of words in "str" into the word list at "wrdlst".
  386.      * A "word" is a sequence of characters delimited by one or more
  387.      * of the characters found in the string "delim".
  388.      * Returns the number of words parsed.
  389.      * CAUTION: the memory for the words in "wrdlst" is allocated
  390.      * by malloc() and should eventually be returned by free()...
  391.      */
  392.     int wrdcnt, wrdlen;
  393.     char wrdbuf[ MAXLINELEN ], c;
  394.  
  395.     wrdcnt = 0;
  396.     while ( *str )
  397.     {
  398.         while ( instr( *str, delim ) )
  399.             ++str;
  400.         if ( !*str )
  401.             break;
  402.         wrdlen = 0;
  403.         while ( (c = *str) && !instr( c, delim ) )
  404.         {
  405.             wrdbuf[ wrdlen++ ] = c;
  406.             ++str;
  407.         }
  408.         wrdbuf[ wrdlen++ ] = 0;
  409.         /*
  410.          * NOTE: allocate a MAXLINELEN sized buffer for every
  411.          * word, just in case user wants to copy a larger string
  412.          * into a field.
  413.          */
  414.         wrdlst[ wrdcnt ] = getmem( MAXLINELEN );
  415.         strcpy( wrdlst[ wrdcnt++ ], wrdbuf );
  416.     }
  417.  
  418.     return wrdcnt;
  419. }
  420.  
  421. void unparse( wrdlst, wrdcnt, str, delim )
  422. char *wrdlst[];
  423. int wrdcnt;
  424. char *str;
  425. char *delim;
  426. {
  427.     /*
  428.      * Replace all the words in "str" with the words in "wrdlst",
  429.      * maintaining the same word seperation distance as found in
  430.      * the string.
  431.      * A "word" is a sequence of characters delimited by one or more
  432.      * of the characters found in the string "delim".
  433.      */
  434.     int wc;
  435.     char strbuf[ MAXLINELEN ], *sp, *wp, *start;
  436.  
  437.     wc = 0;        /* next word in "wrdlst" */
  438.     sp = strbuf;    /* points to our local string */
  439.     start = str;    /* save start address of "str" for later... */
  440.     while ( *str )
  441.     {
  442.         /*
  443.          * Copy the field delimiters from the original string to
  444.          * our local version.
  445.          */
  446.         while ( instr( *str, delim ) )
  447.             *sp++ = *str++;
  448.         if ( !*str )
  449.             break;
  450.         /*
  451.          * Skip over the field in the original string and...
  452.          */
  453.         while ( *str && !instr( *str, delim ) )
  454.             ++str;
  455.  
  456.         if ( wc < wrdcnt )
  457.         {
  458.             /*
  459.              * ...copy in the field in the wordlist instead.
  460.              */
  461.             wp = wrdlst[ wc++ ];
  462.             while ( *wp )
  463.                 *sp++ = *wp++;
  464.         }
  465.     }
  466.     /*
  467.      * Tie off the local string, then copy it back to caller's string.
  468.      */
  469.     *sp = 0;
  470.     strcpy( start, strbuf );
  471. }
  472.  
  473. int instr( c, s )
  474. char c, *s;
  475. {
  476.     while ( *s )
  477.         if ( c==*s++ )
  478.             return 1;
  479.     return 0;
  480. }
  481.  
  482. char *getmem( len )
  483. unsigned len;
  484. {
  485.     char *cp;
  486.  
  487.     if (cp = malloc(len)) return cp;
  488.     error( "out of memory", MEM_ERROR );
  489. }
  490.  
  491. void newfile(s)
  492. char *s;
  493. {
  494.     Linecount = 0;
  495.     if ( Filename = s )
  496.     {
  497.         if ( !(Fileptr = fopen( s, "r" )) )
  498.             error( "file not found", FILE_ERROR );
  499. #ifdef DEBUG
  500.         else if ( Debug )
  501.             printf ("File %s is open now.\n", s);
  502. #endif
  503.     }
  504.     else
  505.     {
  506.         /*
  507.          * No file name given - process standard input.
  508.          */
  509.         Fileptr = stdin;
  510.         Filename = "standard input";
  511.     }
  512. }
  513.  
  514. int getline()
  515. {
  516.     /*
  517.      * Read a line of text from current input file.     Strip off
  518.      * trailing record seperator (newline).
  519.      */
  520.     int rtn, len;
  521.  
  522.     for ( len=0; len<MAXLINELEN; ++len )
  523.     {
  524.         if ( (rtn = getcharacter()) == *Rcrdsep || rtn == -1 )
  525.             break;
  526.         Linebuf[ len ] = rtn;
  527.     }
  528.     Linebuf[ len ] = 0;
  529.  
  530.     if ( rtn == -1 )
  531.     {
  532.         endfile();
  533.         return 0;
  534.     }
  535.     return 1;
  536. }
  537.  
  538. int getcharacter()
  539. {
  540.     /*
  541.      * Read a character from curren input file.
  542.      * WARNING: your getc() must convert lines that end with CR+LF
  543.      * to LF and EOF marks (like CP/M's ^Z) to a -1.
  544.      * Also, getc() must return a -1 when attempting to read from
  545.      * an unopened file.
  546.      */
  547.     int c;
  548.  
  549. #ifdef DEBUG
  550.     if (Debug)
  551.         printf ("getcharacter() is called\n");
  552. #endif
  553.     if ((c = fgetc(Fileptr)) == *Rcrdsep )
  554.         ++Recordcount;
  555. #ifdef DEBUG
  556.     if (Debug)
  557.         printf ("after fgetc() is called\n");
  558. #endif
  559.     if ( c=='\n' )
  560.         ++Linecount;
  561.  
  562. #ifdef DEBUG
  563.     if (Debug)
  564.         putchar (c);
  565. #endif
  566.     return c;
  567. }
  568.  
  569. int ungetcharacter( c )
  570. {
  571.     /*
  572.      * Push a character back into the input stream.
  573.      * If the character is a record seperator, or a newline character,
  574.      * the record and line counters are adjusted appropriately.
  575.      */
  576.     if ( c == *Rcrdsep )
  577.         --Recordcount;
  578.     if ( c=='\n' )
  579.         --Linecount;
  580.     return ungetc( c, Fileptr );
  581. }
  582.  
  583. void endfile()
  584. {
  585.     fclose( Fileptr );
  586.     Filename = NULL;  Linecount = 0;
  587. }
  588.  
  589. void error( s, severe )
  590. char *s;
  591. int severe;
  592. {
  593.     if ( Filename )
  594.         fprintf( stderr, "%s:", Filename );
  595.  
  596.     if ( Linecount )
  597.         fprintf( stderr, " line %d:", Linecount );
  598.  
  599.     fprintf( stderr, " %s\n", s );
  600.     if ( severe )
  601.         exit( severe );
  602. }
  603.  
  604. void usage()
  605. {
  606.     error( "Usage: bawk <actfile> [<file> ...]\n", USAGE_ERROR );
  607. }
  608.  
  609. void movemem( from, to, count )
  610. char *from, *to;
  611. int count;
  612. {
  613.     while ( count-- > 0 )
  614.         *to++ = *from++;
  615. }
  616.  
  617. void fillmem( array, count, value )
  618. char *array, value;
  619. int count;
  620. {
  621.     while ( count-- > 0 )
  622.         *array++ = value;
  623. }
  624.  
  625. int alpha( c )
  626. char c;
  627. {
  628.     return isalpha(c) || c == '_';
  629. }
  630.  
  631. int alphanum( c )
  632. char c;
  633. {
  634.     return isalnum(c) || c == '_';
  635. }
  636.